Skip to content

Harden dailyInfo validation, coordinator parsing, per-day sensor gating, and improve test isolation#67

Merged
eXPerience83 merged 2 commits into1.9.2from
codex/prevent-recreating-disabled-d+1/d+2-sensors
Feb 21, 2026
Merged

Harden dailyInfo validation, coordinator parsing, per-day sensor gating, and improve test isolation#67
eXPerience83 merged 2 commits into1.9.2from
codex/prevent-recreating-disabled-d+1/d+2-sensors

Conversation

@eXPerience83
Copy link
Owner

@eXPerience83 eXPerience83 commented Feb 21, 2026

User description

Motivation

  • Prevent malformed dailyInfo payloads from causing day-offset reindexing or accidental entity churn by aligning config-flow validation with runtime parsing rules.
  • Ensure the coordinator treats non-list or mixed/invalid dailyInfo payloads as malformed and either preserves prior data or fails cleanly on first refresh.
  • Avoid creating D+1/D+2 sensors when effective forecast_days options do not permit them and reduce accidental unique-id collisions for nearby coordinates.
  • Improve test isolation to avoid module-level stub cross-contamination between tests.

Description

  • Config flow now treats an HTTP-200 response as invalid unless dailyInfo is a non-empty list of dict objects and logs/wraps the error via the existing cannot_connect placeholder (custom_components/pollenlevels/config_flow.py).
  • Coordinator parsing now treats dailyInfo as invalid when it is not a list or when any list item is not a dict, preserves the last successful dataset when present, and raises UpdateFailed on first refresh when no prior data exists (custom_components/pollenlevels/coordinator.py).
  • Sensor setup uses a validated local data snapshot for iteration and skips creating *_d1/*_d2 sensors when runtime flags and forecast_days disallow them, and proactively cleans up stale per-day entities (custom_components/pollenlevels/sensor.py).
  • Integration setup normalizes stored forecast sensor mode via normalize_sensor_mode and gates create_d1/create_d2 by the effective forecast_days, and config-flow coordinate unique-id precision was increased to 6 decimals while rejecting whitespace-only API keys (custom_components/pollenlevels/__init__.py and config_flow.py).
  • Tests updated and added: new config-flow regression tests for non-list and non-dict dailyInfo shapes; coordinator tests for invalid/mixed dailyInfo behavior and preservation of last data; sensor test ensuring disabled D+ sensors are not recreated; and setup tests were switched to scoped monkeypatch usage to avoid persistent module reassignment (tests/*).
  • CHANGELOG.md updated under the existing 1.9.3 section to reflect these hardening and test-isolation changes (no version bump).

Testing

  • Ran import-order fix and import checks with ruff check --fix --select I and ruff check on the modified files, and they passed.
  • Formatted changed files with black and applied the reformat to the targeted files successfully.
  • Executed the full test suite with pytest -q and all tests passed (138 passed).

Codex Task


PR Type

Enhancement, Bug fix, Tests


Description

  • Hardened dailyInfo validation in config flow and coordinator to reject non-list/non-dict payloads and preserve last successful data

  • Gated D+1/D+2 sensor creation by effective forecast_days to prevent disabled sensors from being recreated

  • Increased unique-id precision from 4 to 6 decimals to reduce collisions for nearby coordinates

  • Normalized stored forecast sensor mode values during setup and improved test isolation with scoped monkeypatching


Diagram Walkthrough

flowchart LR
  A["Config Flow Validation"] -->|"Validate dailyInfo<br/>is non-empty list of dicts"| B["Reject Invalid Payloads"]
  C["Coordinator Parsing"] -->|"Check dailyInfo type<br/>and item types"| D["Preserve Last Data<br/>or Raise UpdateFailed"]
  E["Setup Entry"] -->|"Normalize mode +<br/>Gate by forecast_days"| F["Create D+1/D+2 Flags"]
  F -->|"Pass flags to sensor setup"| G["Skip Disabled Sensors"]
  H["Unique ID Generation"] -->|"6 decimal precision"| I["Reduce Collisions"]
Loading

File Walkthrough

Relevant files
Enhancement
__init__.py
Gate per-day sensor creation by forecast days                       

custom_components/pollenlevels/init.py

  • Added normalize_sensor_mode() call to handle legacy/whitespace-padded
    forecast mode values
  • Gated create_d1 and create_d2 flags by minimum forecast_days
    thresholds (2 and 3 respectively)
  • Added validation to reject whitespace-only API keys with
    ConfigEntryAuthFailed
+6/-3     
config_flow.py
Strengthen dailyInfo validation and unique-id precision   

custom_components/pollenlevels/config_flow.py

  • Increased unique-id coordinate precision from 4 to 6 decimals to
    reduce collisions for nearby locations
  • Enhanced dailyInfo validation to require non-empty list of dict
    objects instead of just checking presence
  • Added explicit type checks for dailyInfo structure (list + all items
    are dicts)
+15/-3   
Bug fix
coordinator.py
Validate dailyInfo structure and preserve last data           

custom_components/pollenlevels/coordinator.py

  • Added type validation for dailyInfo to ensure it is a list with all
    dict items
  • Treats mixed/invalid dailyInfo structures as malformed instead of
    compacting/reindexing
  • Preserves last successful data when payload is invalid and raises
    UpdateFailed only on first refresh
  • Updated warning/error messages to clarify "missing or invalid"
    dailyInfo
+10/-3   
sensor.py
Skip disabled per-day sensors during setup                             

custom_components/pollenlevels/sensor.py

  • Changed iteration to use validated local data snapshot instead of
    coordinator.data directly
  • Added explicit skipping of *_d1 and *_d2 keys when allow_d1/allow_d2
    flags are False
  • Prevents recreation of disabled per-day sensors during setup
+5/-1     
Tests
test_config_flow.py
Add dailyInfo validation and unique-id precision tests     

tests/test_config_flow.py

  • Added regression test for non-list dailyInfo in HTTP 200 response
    (should set cannot_connect error)
  • Added regression test for dailyInfo list with non-dict items (should
    set cannot_connect error)
  • Updated unique-id test to expect 6-decimal precision format
  • Added new test to verify nearby coordinates produce distinct
    unique-ids with 6-decimal precision
  • Switched test to use _SequenceSession for multiple HTTP calls instead
    of single-call stub
+92/-2   
test_init.py
Improve test isolation and add setup validation tests       

tests/test_init.py

  • Converted module-level stub reassignments to scoped
    monkeypatch.setattr() calls to improve test isolation
  • Added test for forecast sensor mode normalization with
    whitespace-padded values
  • Added test verifying D+1/D+2 creation is disabled when forecast_days
    is insufficient
  • Removed try/finally blocks in favor of monkeypatch cleanup
+95/-12 
test_sensor.py
Add coordinator and sensor setup validation tests               

tests/test_sensor.py

  • Added test for non-list dailyInfo type raising UpdateFailed on first
    refresh
  • Added test for invalid dailyInfo items preserving last successful
    coordinator data
  • Added test for mixed valid/invalid dailyInfo items being treated as
    malformed payload
  • Added async test verifying disabled D+1/D+2 sensors are not created
    when forecast_days disallows them
+208/-0 
Documentation
CHANGELOG.md
Document validation hardening and test improvements           

CHANGELOG.md

  • Documented hardened dailyInfo validation in config flow and
    coordinator
  • Documented prevention of disabled per-day sensor recreation
  • Documented increased unique-id precision and forecast mode
    normalization
  • Documented test isolation improvements and expanded regression
    coverage
  • Added entries under Fixed and Changed sections for version 1.9.3
+18/-0   

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @eXPerience83, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness and reliability of the pollen levels integration by implementing stricter data validation, improving error handling for malformed API responses, and refining sensor creation logic. It also addresses potential unique ID collisions and improves the overall stability of the test suite through better isolation, ensuring a more predictable and resilient user experience.

Highlights

  • Hardened dailyInfo Validation: The config flow and coordinator now rigorously validate the dailyInfo payload, ensuring it is a non-empty list of dictionary objects. Malformed payloads are treated as invalid, preventing data corruption or unexpected reindexing.
  • Improved Coordinator Robustness: The data coordinator now preserves the last successful dataset when encountering invalid dailyInfo payloads, raising an UpdateFailed error only on the first refresh if no prior data exists, enhancing data stability.
  • Per-Day Sensor Gating: Sensor creation logic has been updated to prevent the accidental creation of D+1/D+2 forecast sensors when the configured forecast_days option does not permit them, and proactively cleans up stale entities.
  • Enhanced Test Isolation: Test suites have been refactored to use scoped monkeypatch for coordinator/client stubs, eliminating module-level stub cross-contamination and improving test reliability.
  • Increased Unique ID Precision: The unique ID generation for config flow coordinates has been increased from 4 to 6 decimal places, significantly reducing the chance of accidental collisions for nearby geographical locations.
  • Normalized Sensor Mode Handling: Integration setup now normalizes stored forecast sensor mode values, preventing legacy or whitespace-padded configurations from silently degrading to a 'none' state.
Changelog
  • CHANGELOG.md
    • Aligned config-flow API validation with runtime parsing by requiring dailyInfo to be a non-empty list of objects during setup validation.
    • Prevented test cross-contamination in setup tests by using scoped monkeypatching for coordinator/client stubs instead of persistent module reassignment.
    • Prevented disabled per-day sensors from being re-created during sensor setup by skipping *_d1/*_d2 keys when effective forecast options disable them.
    • Hardened coordinator parsing for malformed dailyInfo payloads by treating non-list/non-dict structures as invalid and preserving the last successful dataset when available.
    • Normalized stored forecast sensor mode values during integration setup so legacy or whitespace-padded values no longer degrade silently to none.
    • Switched sensor setup iteration to use a validated local data snapshot for clearer and more consistent entity creation flow.
    • Increased config-flow coordinate unique-id precision from 4 to 6 decimals to reduce accidental collisions for nearby locations.
    • Expanded regression coverage for disabled per-day sensor creation, malformed dailyInfo handling, setup mode normalization, and nearby-coordinate unique-id behavior.
Activity
  • Import order and checks were fixed and passed using ruff check --fix --select I and ruff check.
  • Changed files were formatted with black and applied successfully.
  • The full test suite was executed with pytest -q, and all 138 tests passed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 21, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 21, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Validate payload structure before parsing

Add a guard to verify payload is a dictionary before calling .get() on it in
_async_update_data to prevent potential errors.

custom_components/pollenlevels/coordinator.py [241-246]

+if not isinstance(payload, dict):
+    raise UpdateFailed("Invalid API payload format")
 daily_raw = payload.get("dailyInfo")
 daily = daily_raw if isinstance(daily_raw, list) else None
 # Keep day offsets stable: if any element is invalid, treat the payload as
 # malformed instead of compacting/reindexing list positions.
 if daily is not None and any(not isinstance(item, dict) for item in daily):
     daily = None
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion improves robustness by adding a type check for the payload variable before it's accessed, preventing potential AttributeError exceptions if the API returns an unexpected non-dictionary response.

Medium
Sort data keys for consistency

Iterate over sorted data keys in async_setup_entry to ensure deterministic
sensor creation order.

custom_components/pollenlevels/sensor.py [192-198]

-for code in data:
+for code in sorted(data):
     if code in ("region", "date"):
         continue
     if code.endswith("_d1") and not allow_d1:
         continue
     if code.endswith("_d2") and not allow_d2:
         continue
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This suggestion improves the determinism of sensor creation by sorting the dictionary keys. This is a good practice for ensuring consistent behavior, especially in tests and across different Python versions.

Low
  • Update

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a73370ef2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request significantly improves the robustness of the integration by hardening the validation of the dailyInfo payload in both the config flow and the coordinator. It also correctly gates the creation of forecast sensors based on the available forecast days and improves test isolation using monkeypatch. The increased precision for unique IDs is a welcome change for accuracy, though it requires consideration for existing entries to avoid duplicate configurations.

@eXPerience83 eXPerience83 merged commit abe9fe7 into 1.9.2 Feb 21, 2026
5 checks passed
@eXPerience83 eXPerience83 deleted the codex/prevent-recreating-disabled-d+1/d+2-sensors branch February 21, 2026 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant